package com.liuya.common;

import com.liuya.common.exception.ConvertException;

import java.lang.reflect.Field;
import java.util.*;

/**
 * @author liuya
 * @date 20161016
 */
public class ReflectUtil {

    /**
     * Reflect a javabean, return properties and javatypes.
     *
     * @param clazz
     * @return [properties][javatypes]
     * @throws ClassNotFoundException
     */
    public static String[][] reflectJavaBean(String clazz) {
        List fields = new LinkedList();

        deepReflectJavabeanFields("", clazz, fields);

        String[][] result = new String[fields.size()][2];
        for (int i = 0; i < fields.size(); i++) {
            JavabeanField f = (JavabeanField) fields.get(i);
            String name = f.getName();
            String type = f.getType();
            result[i] = new String[]{name, type};
        }
        return result;
    }

    /**
     * Reflect javabean's fields, if a field is a javabean too, reflect it
     * cascadly.
     *
     * @param prefixFieldName
     * @param javabeanClazz
     * @param javabeanFieldList List<JavabeanField>
     */
    private static void deepReflectJavabeanFields(String prefixFieldName, String javabeanClazz, List javabeanFieldList) {
        List fields = reflectJavaBeanFields(javabeanClazz);
        for (Iterator iter = fields.iterator(); iter.hasNext(); ) {
            Field innerField = (Field) iter.next();

            String innerFieldName = prefixFieldName + innerField.getName();
            String innerFieldClass = innerField.getType().getName();
            if (innerReflectable(innerFieldClass)) {
                deepReflectJavabeanFields(innerFieldName + ".", innerFieldClass, javabeanFieldList);
            } else {
                JavabeanField javabeanField = new JavabeanField(innerFieldName, innerFieldClass);
                javabeanFieldList.add(javabeanField);
            }
        }
    }

    private static boolean innerReflectable(String clz) {
        if (clz.startsWith("java.") || clz.startsWith("javax.") || clz.equals("int") || clz.equals("double") || clz.equals("float") || clz.equals("short") || clz.equals("boolean") || clz.equals("char") || clz.equals("long") || clz.equals("byte")) {
            return false;
        } else {
            return true;
        }
    }

    /**
     * 反射javabean的属性和它父类的属性
     *
     * @param clazz
     * @return List<Field>
     */
    private static List reflectJavaBeanFields(String clazz) {
        Class c;
        try {
            c = Class.forName(clazz);
        } catch (ClassNotFoundException e) {
            throw new ConvertException(e);
        }

        List allFields = new LinkedList();
        allFields.addAll(Arrays.asList(c.getDeclaredFields()));
        while (!c.getSuperclass().equals(Object.class)) {
            Field[] declaredFields = c.getSuperclass().getDeclaredFields();
            allFields.addAll(Arrays.asList(declaredFields));
            c = c.getSuperclass();
        }

        return allFields;
    }

    public static List reflectJavaBeanFieldNames(String clazz) {
        Class c;
        try {
            c = Class.forName(clazz);
        } catch (ClassNotFoundException e) {
            throw new ConvertException(e);
        }

        List allFields = new ArrayList();
        Field[] fields = c.getDeclaredFields();
        for(int i = 0; i < fields.length; i++) {
            allFields.add(fields[i].getName());
        }

        return allFields;
    }

    /**
     * 返回给定类的所有属性，父类中为私有属性无法得到。
     * @param clazz
     * @return
     */
    public static List<String> reflectJavaBeanFieldNames(Class clazz) {
        List<String> allFields = new ArrayList<String>();
        Field[] fields = clazz.getDeclaredFields();
        for(int i = 0; i < fields.length; i++) {
            allFields.add(fields[i].getName());
        }

        return allFields;
    }
}

class JavabeanField {
    private String name;
    private String type;

    JavabeanField(String name, String type) {
        this.name = name;
        this.type = type;
    }

    public String getName() {
        return name;
    }

    public String getType() {
        return type;
    }
}
